#pragma once

#include "string/tstring.h"
#include <filesystem>
namespace fs = std::filesystem;

namespace CodingSea {

#define LOG_INIT(configDir) LogProxy::Init((configDir))
#define LOG_UNINIT() LogProxy::Uninit()
#define LOG_METHOD_TRACE_IMPL(line)                                                                                    \
    LogMethodTrace tmpLogMethodTrace##line(TString(fs::path(_T(__FILE__)).filename()).c_str(), line, _T(__FUNCTION__))
#define LOG_METHOD_TRACE() LOG_METHOD_TRACE_IMPL(__LINE__)
#define LOG_TRACE(format_str, ...) LOG_IMPL(Trace, __LINE__, format_str, __VA_ARGS__)
#define LOG_DEBUG(format_str, ...) LOG_IMPL(Debug, __LINE__, format_str, __VA_ARGS__)
#define LOG_INFO(format_str, ...) LOG_IMPL(Info, __LINE__, format_str, __VA_ARGS__)
#define LOG_WARN(format_str, ...) LOG_IMPL(Warn, __LINE__, format_str, __VA_ARGS__)
#define LOG_ERROR(format_str, ...) LOG_IMPL(Error, __LINE__, format_str, __VA_ARGS__)
#define LOG_FATAL(format_str, ...) LOG_IMPL(Fatal, __LINE__, format_str, __VA_ARGS__)

#define LOG_IMPL(level, line, format_str, ...)                                                                         \
    {                                                                                                                  \
        TString tmpFormatStr##line =                                                                                   \
            std::format(_T("{} [{}:{}] [{}]"), format_str, TString(fs::path(_T(__FILE__)).filename()), __LINE__,       \
                        _T(__FUNCTION__));                                                                             \
        Log##level##Impl(tmpFormatStr##line.c_str(), __VA_ARGS__);                                                     \
    }

class LogProxy {
public:
    static void Init(const TString& config_file);
    static void Trace(const TString& str);
    static void Debug(const TString& str);
    static void Info(const TString& str);
    static void Warn(const TString& str);
    static void Error(const TString& str);
    static void Fatal(const TString& str);
    static void Uninit();
};

#define LOG_TEMPLATE_DEFINE(level)                                                                                     \
    template <typename... Args> void Log##level##Impl(const TCHAR* format, const Args&... args)                        \
    {                                                                                                                  \
        LogProxy::##level(std::vformat(format, MakeTFormatArgs(args...)));                                             \
    }

LOG_TEMPLATE_DEFINE(Trace)
LOG_TEMPLATE_DEFINE(Debug)
LOG_TEMPLATE_DEFINE(Info)
LOG_TEMPLATE_DEFINE(Warn)
LOG_TEMPLATE_DEFINE(Error)
LOG_TEMPLATE_DEFINE(Fatal)

class LogMethodTrace {
public:
    LogMethodTrace(const TCHAR* fileName, int32_t line, const TCHAR* methodName) : m_methodName(methodName)
    {
        LogProxy::Trace(std::format(_T("{} >>>>>>>>>> [{}:{}]"), m_methodName, fileName, line));
    }
    ~LogMethodTrace()
    {
        LogProxy::Trace(m_methodName + _T(" <<<<<<<<<<"));
    }

private:
    TString m_methodName;
};

} // namespace CodingSea